#ifndef CVX_INTRINSIC_SOLVER_H
#define CVX_INTRINSIC_SOLVER_H 1

#include "cvxImage.h"
#include <map>
#include "least_square.h"
#include "cvxQuadTree.h"
#include "cvxAdaptiveQuadTree.h"
using std::map;

const int CIS_SHA_KNOWN = -1;
//const int CIS_SHA_NOT_SOLVE = -2;  //pixel position which don't need to solve
class CvxStereoIntrinsicSolver;

class CvxIntrinsicSolver
{
	friend class CvxStereoIntrinsicSolver;
public:
	CvxIntrinsicSolver();
	virtual ~CvxIntrinsicSolver();

	void setImage(const Mat &img);	
	void computeEdgeMap(float threshold = 0.999);
	void computeMiniShading();
	void undo();  //copy the previous result
	void savePrevious();  //save previous result
	void saveResult();

	//
	void showResult();
	void saveEdgeMap(const string &fileName);

protected:
	Mat m_orgImg;
	

public:
	Mat m_shading;         //final shading, make sure shading is large than minimal shading value
	Mat m_albedo;          //final albedo

	Mat m_edgeMap;         //set which pixel as edge, 255 is edge
	Mat m_miniShading;     //minimal shading value
	

protected:
	Mat m_preShading;      //copy of shading image for undo
	Mat m_preAlbdeo;       //copy of albedo image for undo
};

/*
	CvxLocalSolver solve the shading value in local area
*/
class CvxLocalSolver:public CvxIntrinsicSolver
{
public:
	CvxLocalSolver();
	~CvxLocalSolver();

	//solve the shading value in rectangle by multiple sampling
	bool solveRect(const vector<cv::Point> &pathPts, int penWidth, const int deviationThreshold);
	bool propagate(void);

	//sample rect area in a path
	static bool adaptiveSample(const vector<cv::Point> &pathPts, int penWidth, const Mat &img,
							   vector<cv::Rect> &rectVec, const int deviationThreshold);

	//set index map and local shading map
	//indexmap: -1 solved, -2 don't need to be solved, >0 to be solved.
	static void GetIndexMap(const Mat &strokeMap, int label, const Mat &img, 
							const vector<cv::Rect> &rectVec, Mat &indexMap, 
						    int &unKnownNum, Mat &localShadingmap);

	//assume the local color distribution is in a plane
	//equation 10 in "User-assisted intrinsic images"
	static void GetEquationAtLocalWindow(const Mat &idxMap, const Mat &shadingMap, const Mat &edgeMap, 
										 const Mat &orgImage, 
										 vector<map<int, double>> &matrix_vec, 
										 vector<double> &right_vec, bool needLeft);

	//set equation at edge 
	/************************************************************************/
	/*   0 1 2
		 3 4 5
		 6 7 8
		 (s4-s3)/(I4-I3) = (s5-s4)/(I5-I4)
	*/
	/************************************************************************/
	static void GetEquationAtEdge(const Mat &idxMap, const Mat &shadingMap, const Mat &edgeMap, 
									const Mat &orgImage, 
									vector<map<int, double>> &matrix_vec, 
									vector<double> &right_vec);
};


/************************************************************************/
/* 
	CvxLocalRefineSolver: local refinement in pixel
*/
/************************************************************************/

class CvxLocalRefineSolver:public CvxIntrinsicSolver
{
public:
	CvxLocalRefineSolver();
	~CvxLocalRefineSolver();

	//set initial shading and albedo map
	void setInitShading(const Mat&shading);

	//solve the stroke area as unknown
	bool solveRect(const vector<cv::Point> &pathPts, int penWidth);

	//set index map and local shading map, label is known index
	//indexmap: -1 solved, >0 to be solved.
	static void GetIndexMap(const Mat &strokeMap, int label, Mat &indexMap, int &unKnownNum);

};


#endif